<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>hydrate 1k table rows</title>
		<meta name="description" content="hydrating 1,000 rows" />
		<style>
			.preloadicon {
				display: none;
			}
			.glyphicon-remove:before {
				content: '⨯';
			}
		</style>
	</head>
	<body>
		<template id="template"></template>
		<script type="module">
			import {
				measureName,
				measureMemory,
				testElementText,
				afterFrame,
				afterFrameAsync
			} from './util.js';
			import * as framework from 'framework';
			import { getComponents } from '../src/keyed-children/components.js';
			import { Store } from '../src/keyed-children/store.js';

			/** @type {HTMLTemplateElement} */
			const template = document.getElementById('template');
			const { Main } = getComponents(framework);
			const { createRoot, createElement } = framework;

			const firstRowSel = 'tr:first-child > td:first-child';
			const lastRowSel = 'tr:last-child > td:first-child';

			const baseStore = new Store();
			baseStore.run();

			/**
			 * Delete the old hydrate root and create a new one with a clone of the
			 * template's content
			 */
			function setupHydrateRoot() {
				const hydrateRootId = 'hydrate-root';
				let hydrateRoot = document.getElementById(hydrateRootId);
				if (hydrateRoot) {
					hydrateRoot.remove();
				}

				hydrateRoot = document.createElement('div');
				hydrateRoot.id = hydrateRootId;
				hydrateRoot.appendChild(template.content.cloneNode(true));
				document.body.appendChild(hydrateRoot);
				return hydrateRoot;
			}

			/** Render the app inside the template tag */
			async function initializeTemplate() {
				// Initialize template
				createRoot(template.content).render(
					createElement(Main, { store: baseStore })
				);
				await afterFrameAsync();
			}

			/**
			 * Click the second row's remove link and ensure the number of rows before
			 * and after the click are as expected
			 */
			async function clickRemove(root, label, expectedBefore, expectedAfter) {
				let rowCount = root.querySelectorAll('tr').length;
				if (rowCount !== expectedBefore) {
					throw new Error(
						`${label}: Incorrect number of rows before remove click. Expected ${expectedBefore} but got ${rowCount}`
					);
				}

				const removeLink = root.querySelector(
					'tr:nth-child(2) td:nth-child(3) a'
				);
				removeLink.click();
				await afterFrameAsync();

				rowCount = root.querySelectorAll('tr').length;
				if (rowCount !== expectedAfter) {
					throw new Error(
						`${label}: Incorrect number of rows after after remove click. Expected ${expectedAfter} but got ${rowCount}`
					);
				}
			}

			async function warmupRun(i) {
				// Test out hydrate and ensure it works
				const hydrateRoot = setupHydrateRoot();

				// Verify initial hydrate root isn't already hydrated and is static
				testElementText(firstRowSel, '1');
				testElementText(lastRowSel, '1000');
				await clickRemove(hydrateRoot, `WARMUP ${i} - prehydrate`, 1000, 1000);

				const store = new Store();
				store.data = baseStore.data.slice();
				createRoot(hydrateRoot).hydrate(createElement(Main, { store }));

				// Verify hydrate has correct markup and is properly hydrated
				testElementText(firstRowSel, '1');
				testElementText(lastRowSel, '1000');
				await clickRemove(hydrateRoot, `WARMUP ${i} - posthydrate`, 1000, 999);
			}

			function timedRun() {
				afterFrame(function () {
					performance.mark('stop');
					performance.measure(measureName, 'start', 'stop');

					measureMemory();
				});

				const hydrateRoot = setupHydrateRoot();
				const store = new Store();
				store.data = baseStore.data.slice();

				performance.mark('start');
				createRoot(hydrateRoot).hydrate(createElement(Main, { store }));
			}

			async function main() {
				// const WARMUP_COUNT = 5;
				const WARMUP_COUNT = 5;
				for (let i = 0; i < WARMUP_COUNT; i++) {
					await warmupRun(i);
				}

				await afterFrameAsync();

				timedRun();
			}

			initializeTemplate().then(main);
		</script>
	</body>
</html>
